/**
 * Copyright (c) 2018, 西安星沙网络科技-版权所有
 *
 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.gnu.org/licenses/lgpl-3.0.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.waleychain.exchange.service.impl.market;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;

import cn.waleychain.exchange.core.SysParaKey;
import cn.waleychain.exchange.core.cache.CacheConts;
import cn.waleychain.exchange.core.constant.DDIC;
import cn.waleychain.exchange.core.entity.PageInfo;
import cn.waleychain.exchange.core.logger.LoggerHelper;
import cn.waleychain.exchange.core.result.RetResultCode;
import cn.waleychain.exchange.core.utils.PageHelper;
import cn.waleychain.exchange.core.utils.StringUtils;
import cn.waleychain.exchange.core.vaildate.VaildateHelper;
import cn.waleychain.exchange.dao.CoinInfoMapper;
import cn.waleychain.exchange.feign.CoinWalletServiceFeign;
import cn.waleychain.exchange.feign.ConfigServiceFeign;
import cn.waleychain.exchange.model.CoinInfo;
import cn.waleychain.exchange.service.impl.BaseServiceImpl;
import cn.waleychain.exchange.service.market.CoinService;

@Service
public class CoinServiceImpl extends BaseServiceImpl implements CoinService {

	private static final Logger mLog = LoggerFactory.getLogger(CoinServiceImpl.class);
	
	@Autowired
	private CoinInfoMapper coinMapper;
	
	@Autowired
	private ConfigServiceFeign configFeign;
	
	@Autowired
	private CoinWalletServiceFeign coinWalletFeign;
	
	@Override
	public long fetchBtcxCoinId() {

		String s = null;
		try {
			s = configFeign.fetchSystemConfigValue(SysParaKey.SYS_CONFIG_BTCX_COIN_ID);
		} catch (Exception e) {
		}
			
		return StringUtils.isBlank(s) ? 0L : Long.parseLong(s);
	}
	
	@Override
	public PageInfo<CoinInfo> fetchCoinPageList(String name, String title, Integer coinType, Integer status, 
			int showCount, int currentPage) throws Exception {

		PageHelper.startPage(currentPage, showCount);
		Page<CoinInfo> page = coinMapper.fetchCoinPageList(name, title, coinType, status);
		
		return new PageInfo<>(page);
	}

	@Override
	@Transactional
	public boolean addCoinInfo(CoinInfo coin) throws Exception {

		// 验证币种是否已存在
		CoinInfo coinInfo = coinMapper.fetchCoinByName(coin.getName());
		VaildateHelper.vaildateBooleanResult(coinInfo != null, RetResultCode.E11001, "该币种名称已添加");
		
		// 系统正在运行交易，不能添加币种
		Integer status = Integer.parseInt(configFeign.fetchSystemConfigValue(SysParaKey.TRADE_CONFIG_SYSTEM_STATUS));
		VaildateHelper.vaildateBooleanResult(DDIC.Boolean.BOOL_TRUE_1.id == status, RetResultCode.E30001);
		
		// 插入币
		boolean bool1 = coinMapper.insertSelective(coin) > 0;
		if (bool1) {
			// 给每个用户插入币种账号
			this.coinWalletFeign.batchInsertUserCoinWallet(coin.getCoinId());

			// 刷新币库
			this.coinWalletFeign.refreshAcceptor(coin);
			
			// 刷新缓存中的币种列表
			this.resetCacheCoinList(true);
		}
		
		return true;
	}

	@Override
	public CoinInfo fetchCoinById(Long coinId) throws Exception {
		
		return coinMapper.fetchCoinById(coinId);
	}

	@Override
	public boolean setCoinStatus(Long coinId, Integer status) throws Exception {
		
		CoinInfo coin = new CoinInfo();
		coin.setCoinId(coinId);
		coin.setStatus(status);
		coin.setModifiedTime(new Date());
		
		boolean bool = coinMapper.updateByPrimaryKeySelective(coin) > 0;
		if (bool) {
			// 刷新币库
			
			// 刷新缓存币种列表
			this.resetCacheCoinList(true);
		}
		
		return bool;
	}

	@Override
	public boolean updateCoinInfo(CoinInfo coin) throws Exception {

		boolean bool = coinMapper.updateByPrimaryKeySelective(coin) > 0;
		if (bool) {
			// 刷新币库
			
			// 刷新缓存币种列表
			this.resetCacheCoinList(true);
		}
		
		return bool;
	}

	@Override
	public void resetCacheCoinList(boolean isForce) throws Exception {
		
		// 获取锁
		boolean isLocked = false;
		try {
			isLocked = this.redisService.lock(CacheConts.CACHE_STATIC_LIST_TRADE_COIN, CacheConts.LOCK_TRADE_COIN_KEY, true);
			if (!isLocked) {
				// 未获取到锁
				return;
			}
			
			// 从缓存中获取数据
			List<CoinInfo> obj = this.getCacheCoinAllList();
			if (obj == null || isForce) {
				// 数据库中获取
				List<CoinInfo> list = coinMapper.fetchCoinAllList();
				Iterator<CoinInfo> iter = list.iterator();
				while (iter.hasNext()) {
					CoinInfo coin = iter.next();
					this.redisService.hset(CacheConts.CACHE_STATIC_LIST_TRADE_COIN, String.valueOf(coin.getCoinId()), coin);
				}
			}
			
		} catch (Exception e) {
			LoggerHelper.printLogErrorNotThrows(mLog, e, "重置刷新币种到缓存异常");
		} finally {
			if (isLocked) {
				// 释放锁
				this.redisService.unlock(CacheConts.CACHE_STATIC_LIST_TRADE_COIN, CacheConts.LOCK_TRADE_COIN_KEY);
			}
		}
	}

	@Override
	public List<CoinInfo> fetchCacheCoinAllList() throws Exception {

		List<CoinInfo> list = this.getCacheCoinAllList();
		if (list == null) {
			this.resetCacheCoinList(false);
			list = this.getCacheCoinAllList();
		}
		
		return list;
	}
	
	private List<CoinInfo> getCacheCoinAllList() {
		Set<String> set = this.redisService.hkey(CacheConts.CACHE_STATIC_LIST_TRADE_COIN);
		if (set == null || set.isEmpty()) {
			return null;
		}
		
		List<CoinInfo> list = new ArrayList<>();
		for (String key : set) {
			String res = this.redisService.hget(CacheConts.CACHE_STATIC_LIST_TRADE_COIN, key);
			CoinInfo coin = JSONObject.parseObject(res, CoinInfo.class);
			list.add(coin);
		}
		
		return list;
	}

	@Override
	public List<CoinInfo> fetchCacheCoinVaildList() throws Exception {

		List<CoinInfo> rs = this.fetchCacheCoinAllList();
		if (rs != null) {
			Iterator<CoinInfo> iter = rs.iterator();
			while (iter.hasNext()) {
				CoinInfo coin = iter.next();
				if (DDIC.CoinStatus.ENABLED.id != coin.getStatus()) {
					iter.remove();
				}
			}
		}

		return rs;
	}

	@Override
	public List<CoinInfo> fetchCoinList(Integer coinType, Integer walletType, Integer status) throws Exception {

		return coinMapper.fetchCoinList(coinType, walletType, status);
	}

}
